package tfts

import (
	"fmt"
	"strconv"
	"strings"
)

type Hook struct {
	event Event
	next  *Hook
}

type buffMeta struct {
	Hook
	attrMeta
	duration int  // 持续次数
	lock     bool // lock=ture->buff期间锁定法力值
	rule     int  // rule=0->添加buff时刷新buff; rule>=1->添加buff时可叠加
}

type buff struct {
	*buffMeta
	key      string
	attached *Fight
	remain   int // 施法次数、攻击次数剩余
	end      int // 时间剩余，需要高精度所以不使用remain
}

func NewBuff(event Event, duration int, attrs ...*attrMeta) *buffMeta {
	meta := buffMeta{
		attrMeta: attrMeta{factor: 100, fighter: &fighter{}},
		Hook:     Hook{event: event},
		duration: duration,
	}
	for _, a := range attrs {
		meta.Add(a)
	}
	return &meta
}

func (h *Hook) match(flag Event) bool {
	if h.event == flag {
		return true
	}

	if h.next != nil {
		return h.next.match(flag)
	}

	return false
}

func (h *Hook) String() string {
	sb := strings.Builder{}
	sb.WriteString(h.event.String())
	for curr := h.next; curr != nil; curr = curr.next {
		sb.WriteString("|")
		sb.WriteString(curr.event.String())
	}
	return sb.String()
}

func (bm *buffMeta) Repeatable() {
	bm.rule += 1
}

func (bm *buffMeta) parseAttr(attrStr string) *buffMeta {
	splits := strings.Split(attrStr, " ")
	// 默认buff有法力锁
	bm.lock = true
	for _, attr := range splits {
		units := strings.Split(attr, "_")
		if len(units) == 1 {
			switch units[0] {
			case "":
			case "free":
				bm.lock = false
			case "dup":
				bm.rule = 1
			default:
				panic(fmt.Sprintf("unknown attr: %s", attr))
			}
			continue
		}
		tag, num := units[0], units[1]
		if call, ok := buffParser[tag]; ok {
			i, err := strconv.Atoi(num)
			if err != nil {
				panic(fmt.Sprintf("illegal num: %s", num))
			}
			bm.Add(call(i))
		} else {
			panic(fmt.Sprintf("unknown attr: %s", attr))
		}
	}
	return bm
}

func (bm *buffMeta) addHook(event Event) *buffMeta {
	hook := &Hook{event: event}
	tail := &bm.Hook
	for ; tail.next != nil; tail = tail.next {
	}
	tail.next = hook
	return bm
}

func (bm *buffMeta) String() string {
	if bm.rule != 0 {
		bm.rule++
	}
	return fmt.Sprintf("%s-%d[%d] %s", bm.Hook.String(), bm.duration, bm.rule, bm.attrMeta.String())
}

func (bm *buffMeta) attach(f *Fight) *buff {
	foo := &buff{
		buffMeta: bm,
		attached: f,
	}
	if bm.match(TimeGoE) {
		foo.end = f.ticks + bm.duration*tick
	} else {
		foo.remain = bm.duration
	}

	key := foo.String()
	foo.key = key
	bf, exist := f.buffs[key]
	if exist {
		bf.remain = foo.duration
		bf.end = foo.end
		foo = bf
		return foo
	}

	f.buffs[key] = foo
	f.attrs = append(f.attrs, foo)
	f.handlers = append(f.handlers, foo)
	if bm.lock {
		f.locks = append(f.locks, foo)
	}

	return foo
}

func (b *buff) valid() bool {
	if b.match(TimeGoE) {
		return b.end >= b.attached.ticks
	}
	return b.remain > 0 && b.factor > 0
}

func (b *buff) handle(e Event) {
	if b.match(e) {
		b.remain--
	}
}
